package codegen.builders;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import uk.co.q3c.deplan.client.util.Q3StringUtil;

/**
 * @see CodegenMethod
 * @see CodegenConstructor
 * @author David Sowerby 19 May 2010
 * 
 */
public class CodegenMethodBase implements Comparable<CodegenMethodBase> {

	private String name;
	protected Visibility visibility = Visibility.PUBLIC;
	private final List<String> body;
	protected final TreeMap<String, Class<?>> parameters;
	protected final CodegenType parent;
	protected String returns = "void";
	private int indents;

	public CodegenMethodBase(CodegenType parent, String name) {
		super();
		this.parent = parent;
		this.name = name;
		body = new ArrayList<String>();
		parameters = new TreeMap<String, Class<?>>();
	}

	public String getName() {
		return name;
	}

	public CodegenMethodBase name(String name) {
		this.name = name;
		return this;
	}

	public Visibility getVisibility() {
		return visibility;
	}

	public CodegenMethodBase visibility(Visibility visibility) {
		this.visibility = visibility;
		return this;
	}

	public CodegenMethodBase addBodyLine(String bodyLine) {
		body.add(bodyLine);
		return this;
	}

	public CodegenMethodBase addParameter(Class<?> clazz, String name) {
		if (parent != null) {
			parent.addImport(clazz);
		}
		parameters.put(name, clazz);
		return this;
	}

	protected void writeParams(BufferedWriter fw) throws IOException {
		boolean firstEntry = true;
		for (Map.Entry<String, Class<?>> entry : parameters.entrySet()) {
			if (!firstEntry) {
				fw.write(",");
			}
			fw.write(entry.getValue()
					.getSimpleName() + " ");
			fw.write(entry.getKey());
			firstEntry = false;
		}
	}

	protected void writeBody(BufferedWriter fw) throws IOException {
		for (String bodyLine : body) {
			parent.writeIndent(fw);
			fw.write(bodyLine);
			fw.write(parent.term);
		}
	}

	@Override
	public int compareTo(CodegenMethodBase other) {
		return this.getName()
				.compareTo(other.getName());
	}

	public void write(BufferedWriter fw) throws IOException {
		doWrite(name, fw);
	}

	protected void doWrite(String methodName, BufferedWriter fw) throws IOException {
		writeDeclaration(methodName, fw);
		fw.write("{\n");
		parent.indent++;
		writeBody(fw);
		parent.indent--;
		parent.writeIndent(fw);
		fw.write("}\n");
	}

	public String getReturns() {
		return returns;
	}

	protected void writeDeclaration(String methodName, BufferedWriter fw) throws IOException {
		String shortReturn = Q3StringUtil.simpleNameFromClassName(returns);
		fw.write(visibility.getText() + " " + shortReturn + " " + methodName + "(");
		writeParams(fw);
		fw.write(")");

	}

	public CodegenMethodBase returns(Class<?> returns) {
		if (parent != null) {
			parent.addImport(returns);
		}
		this.returns = returns.getName();
		return this;
	}

	public String invocationCode(String caller, String[] paramNames) {
		String s = (caller == null) ? "" : caller + ".";
		StringBuffer buf = new StringBuffer(s + name);
		buf.append("(");
		if (paramNames != null) {
			for (int i = 0; i < paramNames.length; i++) {
				if (i != 0) {
					buf.append(", ");
				}
				buf.append(paramNames[i]);
			}
		}
		buf.append(")");
		return buf.toString();
	}

	public String declarationCode(String[] paramNames) {
		StringBuffer buf = basicDeclaration();
		buf.append(" {\n");
		body(buf);
		tabs(buf);
		buf.append("}");
		return buf.toString();
	}

	private void body(StringBuffer buf) {
		indents++;
		for (String bodyLine : body) {
			tabs(buf);
			buf.append(bodyLine + ";\n");
		}
		indents--;
	}

	public String interfaceCode(String[] strings) {
		StringBuffer buf = basicDeclaration();
		buf.append(";");
		return buf.toString();
	}

	protected StringBuffer basicDeclaration() {
		// if (paramNames.length != parameters.size()) {
		// throw new DefinitionException("Number of parameter names called for declaration must match the parameters defined");
		// }
		indents = 1;
		StringBuffer buf = new StringBuffer();
		tabs(buf);
		buf.append(visibility.getText() + " ");
		buf.append(Q3StringUtil.simpleNameFromClassName(returns) + " ");
		buf.append(name);
		buf.append(" (");
		boolean firstEntry = true;
		for (String k : parameters.keySet()) {
			if (!firstEntry) {
				buf.append(", ");
			}
			buf.append(parameters.get(k)
					.getSimpleName());
			buf.append(" " + k);
			firstEntry = false;
		}
		buf.append(")");
		return buf;
	}

	protected void tabs(StringBuffer buf) {
		for (int i = 0; i < indents; i++) {
			buf.append("\t");
		}
	}

}
